package com.tianquan.miracle.proxy;

import com.tianquan.miracle.aop.annotation.*;
import com.tianquan.miracle.util.MiracleUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author: tianquan
 * @date: 2024-10-08  14:33
 * @Description: 切面代理工厂  代理工厂 是aop之前的写法 目前已废弃
 */
public class AspectBeanProxyFactory {
    // 当前解析的bean的所有方法
    Method[] methods;

    private Before before;
    private After after;
    private Around around;
    private Method beforeMethod;
    private Method afterMethod;
    private Method aroundMethod;

    // 需要代理的对象
    Object object;
    // 需要代理的方法名
    List<String> proxyMethodNameList = new ArrayList<>();
    // 需要代理的全类名
    Class<?> proxyClass;
    // 需要代理的beanName
    String beanName;

    public Object getProxy(Object bean) {
        Class<?> aClass = bean.getClass();
        Aspect annotation = aClass.getAnnotation(Aspect.class);
        if (annotation == null) {
            return bean;
        }
        methods = aClass.getMethods();
        // 解析Before,After,Around注解,拿到注解和注解对应方法
        parseMethods(methods);

        if (before == null && after == null && aroundMethod == null) {
            return bean;
        }

        parseAnnotation(before);
        parseAnnotation(around);
        parseAnnotation(after);

        if (object == null) {
            throw new RuntimeException("The proxy object was not found in the container is " + proxyClass.getName());
        }

        // 生成代理对象并注册到容器中
        Object proxy = PoxyFactory.getProxy(object, proxyMethodNameList, beforeMethod, aroundMethod, afterMethod, bean);
        MiracleUtils.registerBean(beanName, proxy);

        return bean;
    }

    public void parseAnnotation(Annotation annotation) {
        if (annotation != null) {
            Class<? extends Annotation> annotationTypeClass = annotation.annotationType();
            String methodName = null;
            try {
                methodName = (String) annotationTypeClass.getMethod("value").invoke(annotation);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            methodName = methodName.substring(0, methodName.indexOf("("));
            Method method = getMethod(methods, methodName);
            if (method != null) {
                Pointcut pointcut = method.getAnnotation(Pointcut.class);
                if (pointcut != null) {
                    // com.tianquan.miracledemo.test.aop.userServiceImpl.addUser()
                    String proxyAllClassAndMethod = pointcut.value();
                    // 解析需要代理的全类名和方法
                    String proxyClassName = getProxyClassName(proxyAllClassAndMethod);
                    if (proxyMethodNameList.isEmpty()) {
                        proxyMethodNameList.addAll(getProxyMethodName(proxyAllClassAndMethod));
                    }
                    // 拿到目标全类名字节码对象并生成beanName
                    try {
                        proxyClass = MiracleUtils.getClassLoader().loadClass(proxyClassName);
                        beanName = MiracleUtils.createBeanName(proxyClass);
                    } catch (ClassNotFoundException e) {
                        throw new RuntimeException(e);
                    }
                    // 获取需要被代理的对象
                    object = MiracleUtils.getBean(beanName);
                }
            }
        }
    }

    private void parseMethods(Method[] methods) {
        for (Method method : methods) {
            Before before1 = method.getAnnotation(Before.class);
            Around around1 = method.getAnnotation(Around.class);
            After after1 = method.getAnnotation(After.class);
            if (before1 != null && before == null) {
                before = before1;
                beforeMethod = method;
            }
            if (around1 != null && around == null) {
                around = around1;
                aroundMethod = method;
            }
            if (after1 != null && after == null) {
                after = after1;
                afterMethod = method;
            }
        }
    }

    private static List<String> getProxyMethodName(String proxyAllClassAndMethod) {
        // 支持单方法和类全部方法
        if (proxyAllClassAndMethod.contains("(")) {
            // 单方法
            return Arrays.asList(proxyAllClassAndMethod.substring(proxyAllClassAndMethod.lastIndexOf(".") + 1,
                    proxyAllClassAndMethod.lastIndexOf("(")));
        }
        // 全部方法
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class<?> aClass = null;
        try {
            aClass = classLoader.loadClass(proxyAllClassAndMethod);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        // 删除掉Object方法
        List<String> ObjectMethodNameList = Arrays.stream(Object.class.getMethods()).collect(Collectors.toList()).stream().map(Method::getName).collect(Collectors.toList());
        List<String> methodNameList = Arrays.stream(aClass.getMethods()).map(Method::getName).collect(Collectors.toList());
        methodNameList.removeAll(ObjectMethodNameList);
        return methodNameList;
    }

    private static String getProxyClassName(String proxyAllClassAndMethod) {
        if (proxyAllClassAndMethod.contains("(")) {
            return proxyAllClassAndMethod.substring(0, proxyAllClassAndMethod.lastIndexOf("."));
        }
        return proxyAllClassAndMethod;
    }

    private static Method getMethod(Method[] methods, String methodName) {
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                return method;
            }
        }
        return null;
    }

}
